package main

import (
	"sort"
	"strconv"
	"strings"

	"gitee.com/bon-ami/eztools/v4"
)

func list1Field(i int, val string) {
	eztools.ShowStr( /*fldsStrs[i] + "=" + */ val + /*", "*/ "\t")
}

// ListFieldsFrmMap prints contact info out of the map.
//   If changedOnly is true, db and id will be used to get current values and
// prints changed info only.
func ListFieldsFrmMap(db *eztools.Dbs, id int, flds map[string]string, changedOnly bool) (changedFlds []string) {
	var (
		searched [][]string
		err      error
	)
	idStr := strconv.Itoa(id)
	if changedOnly {
		if db == nil || id == eztools.InvalidID {
			eztools.Log("NO database or ID provided to list contact info!")
			return
		}
		searched, err = db.Search(tblContacts,
			fldId+"="+idStr,
			fldsStrs, "")
		if err != nil && err != eztools.ErrNoValidResults {
			eztools.Log(err)
			return
		}
	}
	if len(searched) > 1 {
		eztools.Log(strconv.Itoa(len(searched)) + " records found for ID " + idStr)
	}
	eztools.ShowStr("ID=" + idStr)
	for i, v := range fldsStrs {
		f1 := flds[v]
		if changedOnly {
			changed := false
			for _, s1 := range searched {
				if f1 != s1[i] {
					eztools.ShowStr("\t" + v + ": " + s1[i] + " -> " + f1)
					changed = true
				}
			}
			if changed {
				changedFlds = append(changedFlds, v)
			}
		} else if len(f1) > 0 {
			eztools.ShowStr("\t" + v + ": " + f1)
		}
	}
	return
}

// ChooseField asks user to choose a field in contacts table
func ChooseField(info string) string {
	if len(info) > 0 {
		eztools.ShowStrln(info)
	}
	if ret, res := eztools.ChooseStrings(fldsStrs); ret != eztools.InvalidID {
		return res
	}
	return ""
}

// List shows all members' info of a team
func ListContacts(db *eztools.Dbs, team int) error {
	var cri string
	if team > 0 {
		cri = fldsStrs[fldsIndTeam] + "=" + strconv.Itoa(team)
	}
	selected, err := db.Search(tblContacts, cri,
		append(fldsStrs, fldId), "")
	if err != nil {
		return err
	}
	teamArr, err := db.Search(tblTeam, "",
		[]string{fldId, fldStr, fldLeader}, "")
	if err != nil {
		return err
	}
	type teamCont struct {
		str, leader string
	}
	teamMap := make(map[string]teamCont)
	for _, ti := range teamArr {
		teamMap[ti[0]] = teamCont{ti[1], ti[2]}
	}
	eztools.ShowStr(fldId + "\t")
	for i := 0; i < len(fldsStrs); i++ {
		if i != fldsIndTeam {
			list1Field(i, fldsStrs[i])
		}
	}
	eztools.ShowStrln(fldsStrs[fldsIndTeam])
	for _, values := range selected {
		list1Field(0, values[len(fldsStrs)]) // 0 will be wrong as a valid param
		for i := 0; i < len(fldsStrs); i++ {
			if i != fldsIndTeam {
				list1Field(i, values[i])
			}
		}
		eztools.ShowStr( /*"team: " + */ teamMap[values[fldsIndTeam]].str)
		if teamMap[values[fldsIndTeam]].leader == values[len(fldsStrs)] {
			eztools.ShowStrln(", leader")
		} else {
			eztools.ShowStrln(", member")
		}
	}
	return nil
}

// CheckAddrNPosition checks whether position is empty for an address
//	validity/freshness of positions are not checked!
// Return value: ErrIncomplete if position needed for an address
func CheckAddrNPosition(detail string, flds []string, lenB4 int) (error, string) {
	//eztools.ShowStrln(lenB4, fldsIndAddr, fldsIndLat, fldsIndLng, flds)
	if fldsIndAddr == eztools.InvalidID ||
		fldsIndLat == eztools.InvalidID ||
		fldsIndLng == eztools.InvalidID ||
		len(flds[fldsIndAddr]) < 1 {
		return nil, detail
	}
	if len(flds[lenB4+fldsIndLat]) < 1 ||
		len(flds[lenB4+fldsIndLng]) < 1 {
		// must len(genFlds) > len(fldsStrs)
		return eztools.ErrIncomplete, detail +
			"position info needed for address for " + flds[lenB4]
	}
	return nil, detail
}

// CheckContacts checks whether any member is associated with no name or team,
//  and whether there are duplicate values among members, except id,
//  which is generated automatically, team, ext and nick
// Return value:
//	ErrOutOfBound if duplicate records found
//	ErrNoValidResults if a record needs name or team
//	ErrIncomplete if position needed for an address
//	other error from db
func CheckContacts(db *eztools.Dbs) (error, string) {
	selected, err := db.Search(tblContacts, "",
		append(append(fldsStrs, genFlds...), fldId), "")
	if err != nil {
		return err, ""
	}
	if len(selected) < 1 {
		return nil, ""
	}
	indexId := len(selected[0]) - 1
	sorted := sort.IntSlice(append(keyFlds, mustFlds...))
	sorted.Sort()
	if len(selected[0])-1 < sorted[sorted.Len()-1] {
		panic("key/must fields more than current fields")
	}
	type mapStr2Stru map[string]struct{}
	distMaps := make([]mapStr2Stru, len(keyFlds))
	for i := range keyFlds {
		distMaps[i] = make(mapStr2Stru)
	}
	lenManualFlds := len(fldsStrs)
	var detail, detailOnValid string
	for _, j := range selected {
		for _, i := range mustFlds {
			var detailSub string
			if len(j[i]) < 1 {
				detailSub = "NO " + fldsStrs[i] + " found"
				detailOnValid = "\nvalid fields are " + fldsStrAs1 + "," + fldId

			} else {
				if jI, ok := strconv.Atoi(j[i]); ok == nil && jI == 0 {
					detailSub = fldsStrs[i] + " is ZERO"
				}
			}
			if len(detailSub) > 0 {
				detail += "\n" + detailSub + " in record ID " +
					j[indexId] + ": " + j[fldsIndNum] + " - " + j[fldsIndName]
				err = eztools.ErrNoValidResults
			}
		}
		for d, i := range keyFlds {
			if len(j[i]) < 1 {
				continue
			}
			//eztools.ShowStrln("checking " + j[i] + " in " + fldsStrs[i])
			_, ok := distMaps[d][j[i]]
			if ok {
				err1, detail := duplicateContactsFound(db,
					detail, fldsStrs[i], j[i])
				if err1 != nil {
					err = err1
					return err1, detail + detailOnValid
				}
			} else {
				distMaps[d][j[i]] = struct{}{}
			}
		}
		var err1 error
		err1, detail = CheckAddrNPosition(detail, j, lenManualFlds)
		if err1 != nil {
			err = err1
		}
	}
	return err, detail + detailOnValid
}

func duplicateContactsFound(db *eztools.Dbs, detailI string, fld,
	val string) (err error, detailO string) {
	detailO = detailI + "\nMULTIPLE records with " + val + " in " + fld
	selected, err := db.Search(tblContacts, fld+"=\""+val+"\"",
		append(fldsStrs, fldId), "")
	if err != nil {
		return
	}
	for _, s1 := range selected {
		detailO += "\n" + strings.Join(s1, ",")
	}
	//eztools.ShowStrln("Remember to correct them!")
	return eztools.ErrOutOfBound, detailO
}
